/*
 * proc_mgmt.cpp
 *
 *  Created on: 15-May-2011
 *      Author: tan-ce
 */

#include "proc_mgmt.h"
#include <vector>

//#include <iostream>

using namespace std;
using namespace tcetools;

void ChildProcess::init() {
	int in_pipe[2], out_pipe[2], err_pipe[2];

	// fd[0] for read, fd[1] for write
	pipe(in_pipe);
	pipe(out_pipe);
	pipe(err_pipe);

	child_pid = fork();

	if (child_pid == -1) { // Error
		ProcessError *pe = new ProcessError("Could not fork new process");
		pe->append_unix_error();
		throw pe;

	} else if (child_pid) { // Parent
		fd_in = in_pipe[1];
		fd_out = out_pipe[0];
		fd_err = err_pipe[0];

		close(in_pipe[0]);
		close(out_pipe[1]);
		close(err_pipe[1]);

		s_in = new Socket(fd_in);
		s_out = new Socket(fd_out);
		s_err = new Socket(fd_err);

		s_in->set_line_delimiter("\n");
		s_in->set_noblock();

		s_out->set_line_delimiter("\n");
		s_out->set_noblock();

		s_err->set_line_delimiter("\n");
		s_err->set_noblock();

	} else { // Child
		// dup2(a, b): make b copy of a -> put standard FDs in b
		dup2(in_pipe[0], STDIN_FILENO);
		dup2(out_pipe[1], STDOUT_FILENO);
		dup2(err_pipe[1], STDERR_FILENO);

		s_in = s_out = s_err = 0;

		close(in_pipe[1]);
		close(out_pipe[0]);
		close(err_pipe[0]);
	}
}

void ChildProcess::exec_cmd(const char *path, char *const argv[]) {
	execvp(path, argv);

	int err_msg_len;
	const char *err_msg = "ChildProcess: execv() call failed\n";

	for (err_msg_len = 0; err_msg[err_msg_len]; err_msg_len++);

	write(STDERR_FILENO, err_msg, err_msg_len);
	fsync(STDERR_FILENO);
	_exit(-1);
}

ChildProcess::ChildProcess(const char *cmd) {
	vector<char *> arg_list;
	char **argv;
	char *args;
	int len;

	for (len = 0; cmd[len]; len++);

	args = new char[len + 1];

	for (len = 0; cmd[len]; len++) {
		char c = cmd[len];

		if (c == ' ') {
			args[len] = 0;

			if (cmd[len + 1] == 0) break;

			while (cmd[len + 1] == ' ') len++;

			arg_list.push_back(&args[len + 1]);
		} else args[len] = c;
	}
	args[len] = 0;

	argv = new char *[arg_list.size()];

	argv[0] = args;

	int i = 1;
	for (vector<char *>::iterator I = arg_list.begin(); I != arg_list.end(); ++I) {
		argv[i] = *I;
	}

	init();

	if (is_child()) {
		exec_cmd(argv[0], argv);
	}
}

ChildProcess::~ChildProcess() {
	if (s_out) delete s_out;
	if (s_err) delete s_err;
	if (s_in) delete s_in;

	if (is_parent()) {
		kill(child_pid, 9);
		waitpid(child_pid, 0, 0);
	}
}
